Системное программирование

Использование GDI32

Системное программирование

Введение:

GDI32.DLL (Graphics Device Interface) - это динамически подключаемая библиотека (DLL) Windows, предоставляющая функции для рисования графики на экране или других устройствах, таких как принтеры. Она является частью API Windows и используется для визуализации графических элементов, текста, изображений и других визуальных представлений. Понимание GDI32 критически важно для системных программистов, разрабатывающих приложения, требующие прямого управления отображением графики.

Использование GDI32
Системное программирование

1. Что такое GDI32?

GDI32 выступает в роли посредника между приложениями и драйверами графических устройств. Приложение вызывает функции GDI32, чтобы нарисовать линию, текст, изображение или другие графические элементы, а GDI32, в свою очередь, взаимодействует с драйвером конкретного устройства, чтобы выполнить эти операции.

Использование GDI32
Системное программирование

История и роль GDI32 в Windows:

GDI (включая GDI32) существует с самых ранних версий Windows (Windows 1.0 в 1985 году). Изначально GDI был ключевым компонентом для отрисовки интерфейса пользователя, отображения графики в приложениях и для печати. Исторически, GDI был практически единственным способом рисовать графику в Windows, что сделало его чрезвычайно важным компонентом для совместимости и функционирования системы. С развитием Windows и появлением более мощных графических аппаратных средств, GDI постепенно уступил часть своей роли новым API, ориентированным на производительность и современные графические возможности.

Использование GDI32
Системное программирование

Место GDI32 в архитектуре Windows:

GDI32 располагается между приложениями и драйверами устройств отображения. Архитектурно это выглядит следующим образом:

  1. Приложение: Вызывает функции GDI32, передавая параметры для рисования (координаты, цвет, шрифт и т.д.).
  2. GDI32.DLL: Получает запросы от приложений, выполняет базовую обработку и транслирует их в команды, понятные драйверам устройств. GDI32 также управляет контекстами устройств (HDC - Handle to Device Context), которые содержат информацию о состоянии графического устройства.
  3. Драйверы устройств: Получают команды от GDI32 и преобразуют их в инструкции для конкретного оборудования (видеокарты, принтера). Драйвер отвечает за фактическое отображение графики на экране или печать на бумаге.
  4. Ядро Windows: GDI32 работает в пользовательском режиме, но для выполнения некоторых операций может вызывать функции ядра. В более старых версиях Windows (до Windows Vista), GDI работала в режиме ядра, что делало ее более быстрой, но и более нестабильной, так как ошибки в GDI могли приводить к сбоям всей системы.
Использование GDI32
Системное программирование

Преимущества и недостатки использования GDI32:

Преимущества:

  • Широкая совместимость: Поддерживается практически всеми версиями Windows, что делает его хорошим выбором для приложений, требующих максимальной совместимости.
  • Простота использования: Многие функции GDI32 относительно просты в использовании, особенно для базовых графических операций.
  • Зрелая технология: GDI32 – это проверенная и надежная технология с большим количеством документации и примеров.
  • Интеграция с Windows: GDI32 тесно интегрирован с операционной системой и многими другими API Windows.
  • Поддержка принтеров: Обеспечивает отличную поддержку печати, что делает его важным для приложений, работающих с документами.
Использование GDI32
Системное программирование

Недостатки:

  • Ограниченная производительность: GDI32 не использует аппаратное ускорение графики в полной мере (хотя некоторые операции могут ускоряться аппаратно). Это может приводить к низкой производительности для сложных графических задач, таких как 3D-графика или обработка больших изображений.
  • Ограниченные возможности: Не поддерживает современные графические эффекты, такие как шейдеры, сложные текстуры и т.д.
  • Проблемы с масштабированием: В старых версиях Windows GDI32 имел проблемы с корректным масштабированием на экранах с высоким разрешением (HiDPI). Хотя эта проблема была частично решена в более поздних версиях, Direct2D предлагает лучшее решение для масштабирования.
  • Рисование в основном на CPU: Большая часть обработки графики выполняется центральным процессором (CPU), что может приводить к его перегрузке при интенсивных графических операциях.
Использование GDI32
Системное программирование

Альтернативы GDI32 (Direct2D, OpenGL):

С развитием графических технологий появились альтернативы GDI32, которые предлагают более высокую производительность и расширенные возможности:

  • Direct2D: API от Microsoft, предназначенный для 2D-графики. Direct2D использует аппаратное ускорение графики (GPU), что значительно повышает производительность по сравнению с GDI32. Он также обеспечивает лучшее качество рендеринга, особенно для текста и векторной графики, и лучше справляется с масштабированием на экранах HiDPI. Direct2D рекомендуется для новых проектов, требующих высокой производительности 2D-графики.
Использование GDI32
Системное программирование
  • OpenGL: Кроссплатформенный API для 2D и 3D графики. OpenGL обеспечивает доступ к аппаратным возможностям графического процессора (GPU) и широко используется в играх, САПР (системы автоматизированного проектирования) и других приложениях, требующих высокой производительности. OpenGL требует более глубоких знаний графики, чем GDI32 или Direct2D, но предоставляет максимальную гибкость и контроль над процессом рендеринга.

  • Direct3D: API от Microsoft, предназначенный для 3D-графики. Является частью DirectX. Direct3D обеспечивает доступ к аппаратным возможностям графического процессора (GPU) и широко используется в играх и других приложениях, требующих высокой производительности 3D-графики.

Использование GDI32
Системное программирование

Выбор между GDI32 и альтернативами зависит от конкретных требований приложения:

  • GDI32: Подходит для простых графических задач, приложений, требующих максимальной совместимости, или приложений, где производительность не является критичным фактором.
  • Direct2D: Рекомендуется для новых проектов, требующих высокой производительности 2D-графики и хорошей поддержки масштабирования.
  • OpenGL/Direct3D: Предназначены для приложений, требующих высокой производительности 3D-графики, таких как игры и САПР.
Использование GDI32
Системное программирование

2. Основные понятия GDI32:

Device Context (DC) - Контекст устройства:

  • Что это такое: Device Context (DC) - это структура данных, которая содержит информацию о графическом устройстве (экран, принтер, память), а также текущие настройки рисования, такие как цвет, шрифт, текущая позиция, и т.д. Представьте DC как "холст" и "палитру художника", где хранятся все необходимые инструменты и настройки для рисования. Он позволяет абстрагироваться от специфики конкретного устройства, предоставляя унифицированный интерфейс для графического вывода.
Использование GDI32
Системное программирование
  • Типы DC:
    • Экранный DC (Screen DC): Представляет экран монитора. Используется для рисования непосредственно на экране.
    • DC памяти (Memory DC): Представляет область памяти, которая может использоваться как буфер для рисования "за кадром". Это особенно полезно для сложной графики, анимации и предотвращения мерцания. Рисуем в памяти, а потом быстро копируем содержимое DC памяти на экран.
    • DC принтера (Printer DC): Представляет принтер. Используется для отправки графики на печать.
Использование GDI32
Системное программирование
  • Функции GetDC() и ReleaseDC():
    • GetDC(HWND hWnd): Получает дескриптор (handle) DC для указанного окна (hWnd). Если hWnd равен NULL, то получает DC для всего экрана. Важно: полученный DC нужно освободить после использования.
    • ReleaseDC(HWND hWnd, HDC hDC): Освобождает DC, полученный с помощью GetDC(). Крайне важно освобождать DC, чтобы предотвратить утечки ресурсов и возможные проблемы с отображением. Не освобожденные DC могут привести к замедлению работы системы и даже к ее нестабильности.
Использование GDI32
Системное программирование

Пример:

HWND hWnd = /* Получаем дескриптор окна */;
HDC hDC = GetDC(hWnd);  // Получаем DC для окна

// Рисуем что-нибудь, используя hDC

ReleaseDC(hWnd, hDC); // Освобождаем DC
Использование GDI32
Системное программирование

Handle (Дескриптор):

  • Понятие дескриптора: Дескриптор (handle) - это числовое значение, которое однозначно идентифицирует ресурс, управляемый операционной системой (окно, файл, DC, кисть, перо и т.д.). Это своего рода "ключ" или "указатель" на внутреннюю структуру данных, содержащую информацию об этом ресурсе. Приложения не работают напрямую с внутренними структурами операционной системы, а взаимодействуют с ними через дескрипторы.

  • Роль в GDI32: В GDI32 дескрипторы используются для идентификации DC (HDC), графических объектов (HBRUSH, HPEN, HFONT, HBITMAP) и других ресурсов. Функции GDI32 принимают дескрипторы в качестве аргументов, чтобы знать, с каким конкретным ресурсом нужно работать.

Использование GDI32
Системное программирование

Graphics Objects (Графические объекты):

  • Кисти (Brushes): Используются для заливки областей (например, прямоугольников, многоугольников, эллипсов). Определяют цвет, узор и стиль заливки.
  • Перья (Pens): Используются для рисования линий и контуров. Определяют цвет, ширину и стиль линии (сплошная, пунктирная, штрихпунктирная и т.д.).
  • Шрифты (Fonts): Используются для отображения текста. Определяют начертание, размер, стиль и другие характеристики шрифта.
  • Растровые изображения (Bitmaps): Используются для отображения изображений, состоящих из пикселей. Могут быть загружены из файлов или созданы в памяти.
Использование GDI32
Системное программирование

Пример (создание и выбор кисти):

HDC hDC = GetDC(hWnd);
HBRUSH hBrush = CreateSolidBrush(RGB(255, 0, 0)); // Создаем красную кисть
HBRUSH hOldBrush = (HBRUSH)SelectObject(hDC, hBrush); // Выбираем кисть в DC и запоминаем предыдущую

Rectangle(hDC, 10, 10, 100, 100); // Рисуем красный прямоугольник

SelectObject(hDC, hOldBrush); // Возвращаем предыдущую кисть
DeleteObject(hBrush); // Освобождаем кисть
ReleaseDC(hWnd, hDC);
Использование GDI32
Системное программирование

Logical Units (Логические единицы):

  • Понятие логических единиц: Логические единицы - это абстрактные единицы измерения, которые используются в функциях GDI32 для определения размеров и координат графических примитивов. Они позволяют абстрагироваться от физических единиц измерения конкретного устройства (например, пикселей на экране, точек на принтере).

  • Отображение на физические единицы: Операционная система преобразует логические единицы в физические единицы (пиксели, точки) в соответствии с текущим режимом отображения (mapping mode) DC. Это преобразование определяется viewport и window DC.

Использование GDI32
Системное программирование

Coordinate Systems (Системы координат):

  • Система координат в GDI32: GDI32 использует двумерную систему координат.
    • По умолчанию: Начало координат (0, 0) находится в верхнем левом углу клиентской области окна. Ось X направлена вправо, ось Y - вниз.
    • Режимы отображения (Mapping Modes): GDI32 предоставляет различные режимы отображения, которые позволяют изменить систему координат, например, изменить направление осей, масштаб и начало координат. Наиболее часто используемые режимы:
      • MM_TEXT (по умолчанию): 1 логическая единица = 1 пиксель.
      • MM_LOMETRIC, MM_HIMETRIC, MM_LOENGLISH, MM_HIENGLISH: Определяют, сколько логических единиц соответствует 1 мм или 1 дюйму. Полезны для работы с физическими размерами.
      • MM_ISOTROPIC, MM_ANISOTROPIC: Позволяют задать произвольное масштабирование и преобразование координат.
Использование GDI32
Системное программирование

Пример (изменение режима отображения):

HDC hDC = GetDC(hWnd);
SetMapMode(hDC, MM_ANISOTROPIC); // Устанавливаем произвольный режим
SetViewportExtEx(hDC, 100, 100, NULL); // Задаем размер viewport (окна) в пикселях
SetWindowExtEx(hDC, 1000, 1000, NULL); // Задаем размер window (логического пространства)
// Теперь прямоугольник Rectangle(hDC, 0, 0, 500, 500); займет половину viewport

ReleaseDC(hWnd, hDC);
Использование GDI32
Системное программирование

Важные замечания:

  • Освобождайте ресурсы: Всегда освобождайте полученные DC с помощью ReleaseDC(), а созданные графические объекты (кисти, перья, шрифты, растровые изображения) с помощью DeleteObject(), чтобы избежать утечек памяти и других проблем.
  • Обратите внимание на режимы отображения: Выбор правильного режима отображения важен для достижения желаемого результата и обеспечения масштабируемости графики.
  • Двойная буферизация: Для создания более плавной графики и избежания мерцания используйте двойную буферизацию, то есть рисуйте сначала в DC памяти, а затем копируйте содержимое DC памяти на экранный DC.
Использование GDI32
Системное программирование

3. Основные функции GDI32:

GDI32 предоставляет широкий набор функций, сгруппированных по категориям для удобства. Вот подробное описание основных групп функций с пояснениями и примерами:

Использование GDI32
Системное программирование

I. DC Management (Управление контекстами устройств):

  • Что такое DC (Device Context)? Контекст устройства (DC) – это структура данных, которая описывает атрибуты и параметры устройства отображения (например, экран, принтер, память). DC содержит информацию о цвете, шрифте, кистях, перьях и других графических параметрах, которые используются для рисования. По сути, DC - это "холст", на котором вы рисуете.
Использование GDI32
Системное программирование
  • GetDC(HWND hWnd):
    • Описание: Получает контекст устройства для указанного окна (hWnd). Если hWnd равен NULL, функция получает контекст устройства для всего экрана.

    • Использование: Вам нужно получить DC перед тем, как вы сможете что-либо нарисовать в окне или на экране.

    • Пример:

      HDC hdc = GetDC(hWnd); // Получаем DC для окна hWnd
      if (hdc) {
        // Используем hdc для рисования
        ReleaseDC(hWnd, hdc); // Важно освободить DC после использования
      }
      
Использование GDI32
Системное программирование
  • ReleaseDC(HWND hWnd, HDC hDC):
    • Описание: Освобождает контекст устройства, освобождая ресурсы, связанные с ним. Важно освобождать DC после использования, чтобы избежать утечек ресурсов и проблем с другими приложениями.
    • Использование: Всегда вызывайте ReleaseDC после завершения рисования с использованием полученного GetDC.
    • Пример: (см. пример выше для GetDC)
Использование GDI32
Системное программирование
  • BeginPaint(HWND hWnd, LPPAINTSTRUCT lpPaint) и EndPaint(HWND hWnd, const PAINTSTRUCT *lpPaint):
    • Описание: Эти функции используются только при обработке сообщения WM_PAINT. BeginPaint подготавливает окно для рисования, а EndPaint завершает рисование и сообщает системе, что окно перерисовано. BeginPaint автоматически получает DC и валидирует регион обновления окна.
    • Использование: Обязательно используйте их в вашей процедуре обработки окна (WindowProc), когда получаете сообщение WM_PAINT.
    • Пример:
Использование GDI32
Системное программирование
case WM_PAINT: {
  PAINTSTRUCT ps;
  HDC hdc = BeginPaint(hWnd, &ps);

  // Используем hdc для рисования
  // Например: TextOut(hdc, 10, 10, "Hello, World!", 13);

  EndPaint(hWnd, &ps);
  return 0;
}
break;
Использование GDI32
Системное программирование
  • CreateCompatibleDC(HDC hdc) и DeleteDC(HDC hdc):
    • Описание: CreateCompatibleDC создает контекст устройства, совместимый с существующим DC (hdc). Он обычно используется для создания контекста устройства в памяти (off-screen buffer). Это позволяет рисовать что-то в памяти (например, в растровом изображении) и затем быстро скопировать это на экран. DeleteDC удаляет созданный контекст устройства.
    • Использование: Off-screen buffer technique (двойная буферизация) – важный метод для предотвращения мерцания при отрисовке сложных графических сцен.
Использование GDI32
Системное программирование

Пример:

HDC hdcScreen = GetDC(hWnd);
HDC hdcMem = CreateCompatibleDC(hdcScreen);

// Создаем растровое изображение для off-screen буфера
HBITMAP hbmMem = CreateCompatibleBitmap(hdcScreen, width, height);
SelectObject(hdcMem, hbmMem); // Выбираем растровое изображение в hdcMem

// Рисуем в hdcMem
Rectangle(hdcMem, 0, 0, width, height); // Закрашиваем фон

// Копируем из hdcMem на экран
BitBlt(hdcScreen, 0, 0, width, height, hdcMem, 0, 0, SRCCOPY);

// Освобождаем ресурсы
DeleteObject(hbmMem);
DeleteDC(hdcMem);
ReleaseDC(hWnd, hdcScreen);
Использование GDI32
Системное программирование

II. Drawing Functions (Функции рисования):

  • MoveToEx(HDC hdc, int x, int y, LPPOINT lpPoint) и LineTo(HDC hdc, int x, int y):
    • Описание: MoveToEx устанавливает текущую позицию для рисования. LineTo рисует линию от текущей позиции до точки (x, y). lpPoint (необязательный) – указатель на структуру POINT, которая получает предыдущую текущую позицию.

    • Использование: Базовые функции для рисования линий и сложных фигур.

    • Пример:

      MoveToEx(hdc, 10, 10, NULL);
      LineTo(hdc, 100, 100); // Рисует линию от (10, 10) до (100, 100)
      
Использование GDI32
Системное программирование
  • Rectangle(HDC hdc, int left, int top, int right, int bottom), Ellipse(HDC hdc, int left, int top, int right, int bottom), RoundRect(HDC hdc, int left, int top, int right, int bottom, int width, int height):
    • Описание: Рисуют прямоугольники, эллипсы и прямоугольники со скругленными углами. left, top, right, bottom определяют ограничивающий прямоугольник для фигуры. Для RoundRect, width и height задают ширину и высоту эллипса, используемого для скругления углов.

    • Использование: Рисование базовых геометрических фигур.

    • Пример:

      Rectangle(hdc, 50, 50, 150, 100); // Рисует прямоугольник
      Ellipse(hdc, 200, 50, 300, 150);   // Рисует эллипс
      RoundRect(hdc, 350, 50, 450, 150, 20, 20); // Рисует прямоугольник с закругленными углами
      
Использование GDI32
Системное программирование
  • Polygon(HDC hdc, const POINT *lpPoints, int nCount), Polyline(HDC hdc, const POINT *lpPoints, int nCount):
    • Описание: Polygon рисует многоугольник, а Polyline рисует ломаную линию. lpPoints – это указатель на массив структур POINT, содержащих координаты вершин. nCount – количество вершин. Polygon автоматически замыкает фигуру, соединяя последнюю точку с первой. Polyline этого не делает.

    • Использование: Рисование сложных форм, состоящих из множества линий.

    • Пример:

      POINT points[] = {{10, 10}, {50, 50}, {100, 10}, {75, 75}};
      Polygon(hdc, points, 4); // Рисует закрашенный многоугольник
      //Polyline(hdc, points, 4); // Рисует ломаную линию
      
Использование GDI32
Системное программирование
  • TextOut(HDC hdc, int x, int y, LPCTSTR lpString, int cch), DrawText(HDC hdc, LPCTSTR lpString, int nCount, LPRECT lpRect, UINT uFormat):
    • Описание: TextOut выводит строку текста в указанных координатах. DrawText предоставляет больше возможностей для форматирования текста (например, выравнивание, перенос по словам) и выводит текст в пределах указанного прямоугольника.

    • Использование: Отображение текста на экране.

    • Пример:

      TextOut(hdc, 10, 200, "Hello from TextOut!", 18);
      
      RECT rect = {10, 250, 200, 300};
      DrawText(hdc, "Hello from DrawText!\nWith multiple lines.", -1, &rect, DT_LEFT | DT_TOP | DT_WORDBREAK);
      
Использование GDI32
Системное программирование
  • SetPixel(HDC hdc, int x, int y, COLORREF color), GetPixel(HDC hdc, int x, int y):
    • Описание: SetPixel устанавливает цвет указанного пикселя. GetPixel получает цвет указанного пикселя.

    • Использование: Работа с отдельными пикселями. Обычно, это медленнее, чем использование растровых изображений для рисования.

    • Пример:

      SetPixel(hdc, 50, 20, RGB(255, 0, 0)); // Устанавливает пиксель в красный цвет
      COLORREF color = GetPixel(hdc, 50, 20); // Получает цвет пикселя
      
Использование GDI32
Системное программирование

III. Graphics Objects Management (Управление графическими объектами):

  • CreatePen(int iStyle, int iWidth, COLORREF color), CreateSolidBrush(COLORREF color), CreateFont(int cHeight, int cWidth, int cEscapement, int cOrientation, int iWeight, DWORD bItalic, DWORD bUnderline, DWORD cStrikeOut, DWORD iCharSet, DWORD iOutPrecision, DWORD iClipPrecision, DWORD iQuality, DWORD iPitchAndFamily, LPCTSTR pszFaceName):
    • Описание: Создают графические объекты: перо (для рисования линий), кисть (для заполнения фигур) и шрифт (для отображения текста). Параметры позволяют настроить внешний вид объектов.
    • Использование: Создание объектов, которые будут использоваться для рисования.
Использование GDI32
Системное программирование

Пример:

HPEN hPen = CreatePen(PS_SOLID, 2, RGB(0, 0, 255)); // Синее перо толщиной 2 пикселя
HBRUSH hBrush = CreateSolidBrush(RGB(255, 255, 0)); // Желтая кисть
HFONT hFont = CreateFont(16, 0, 0, 0, FW_NORMAL, FALSE, FALSE, 
	FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, 
	DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, "Arial"); // Шрифт Arial
Использование GDI32
Системное программирование
  • SelectObject(HDC hdc, HGDIOBJ hgdiobj):
    • Описание: Выбирает графический объект (hgdiobj) в контекст устройства (hdc). После выбора объект становится активным для рисования. Старый объект заменяется, и SelectObject возвращает дескриптор старого объекта. Очень важно сохранить этот дескриптор, чтобы вернуть старый объект обратно в DC после использования нового.
    • Использование: Применение созданных перьев, кистей и шрифтов.
Использование GDI32
Системное программирование

Пример:

HPEN hOldPen = (HPEN)SelectObject(hdc, hPen); // Выбираем новое перо, сохраняем старое
Rectangle(hdc, 10, 10, 100, 50); // Рисуем прямоугольник синим пером

SelectObject(hdc, hOldPen); // Возвращаем старое перо
DeleteObject(hPen);       // Удаляем созданное перо
Использование GDI32
Системное программирование
  • DeleteObject(HGDIOBJ hObject):
    • Описание: Удаляет графический объект (hObject), освобождая ресурсы, связанные с ним.
    • Использование: Освобождение ресурсов после использования перьев, кистей, шрифтов, растровых изображений и т.д. Важно: Не удаляйте объекты, которые все еще выбраны в контексте устройства! Сначала выберите старый объект обратно, а затем удалите созданный.
    • Пример: (см. пример выше для SelectObject)
Использование GDI32
Системное программирование

IV. Bitmap Functions (Функции для работы с растровыми изображениями):

  • CreateBitmap(int nWidth, int nHeight, UINT nPlanes, UINT nBitCount, const VOID *lpBits), CreateDIBSection(HDC hdc, const BITMAPINFO *pbmi, UINT usage, VOID **ppvBits, HANDLE hSection, DWORD dwOffset):
    • Описание: CreateBitmap создает растровое изображение (bitmap) с указанными размерами и параметрами. CreateDIBSection создает DIB (Device Independent Bitmap) – растровое изображение, независимое от устройства. CreateDIBSection позволяет получить прямой доступ к пиксельным данным растрового изображения, что делает его более эффективным для манипулирования пикселями.
    • Использование: Создание растровых изображений для хранения и отображения графики.
Использование GDI32
Системное программирование

Пример:

// Пример CreateDIBSection
BITMAPINFO bmi;
ZeroMemory(&bmi, sizeof(BITMAPINFO));
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmi.bmiHeader.biWidth = 200;
bmi.bmiHeader.biHeight = -100;  // Отрицательная высота - изображение перевернуто (верхний левый угол - начало)
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = 32; // 32 bits per pixel (RGBA)
bmi.bmiHeader.biCompression = BI_RGB;

VOID* pBits;
HBITMAP hBitmap = CreateDIBSection(hdc, &bmi, DIB_RGB_COLORS, &pBits, NULL, 0);

// Теперь pBits указывает на массив пикселей, к которым можно получить доступ и изменять их.
// Например:
DWORD* pixels = (DWORD*)pBits;
for (int i = 0; i < 200 * 100; ++i) {
  pixels[i] = RGB(i % 256, (i * 2) % 256, (i * 3) % 256); // Заполняем случайными цветами
}
Использование GDI32
Системное программирование
  • BitBlt(HDC hdcDest, int xDest, int yDest, int width, int height, HDC hdcSrc, int xSrc, int ySrc, DWORD rop), StretchBlt(HDC hdcDest, int xDest, int yDest, int wDest, int hDest, HDC hdcSrc, int xSrc, int ySrc, int wSrc, int hSrc, DWORD rop):
    • Описание: BitBlt копирует блок пикселей из одного контекста устройства (hdcSrc) в другой (hdcDest). StretchBlt выполняет то же самое, но позволяет растягивать или сжимать изображение во время копирования. rop (raster-operation code) определяет, как исходные пиксели комбинируются с целевыми пикселями.
    • Использование: Копирование и масштабирование растровых изображений.
Использование GDI32
Системное программирование

Пример:

// Копируем растровое изображение hbmMem из контекста hdcMem в контекст hdcScreen
BitBlt(hdcScreen, 0, 0, width, height, hdcMem, 0, 0, SRCCOPY);

// Растягиваем растровое изображение
StretchBlt(hdcScreen, 100, 100, 2 * width, 2 * height, hdcMem, 0, 0, width, height, SRCCOPY);
Использование GDI32
Системное программирование

V. Color Management (Управление цветом):

  • RGB(BYTE red, BYTE green, BYTE blue):
    • Описание: Создает значение цвета (COLORREF) из компонентов красного, зеленого и синего. Каждый компонент – это байт (значение от 0 до 255).

    • Использование: Определение цветов для перьев, кистей, текста и т.д.

    • Пример:

      COLORREF redColor = RGB(255, 0, 0);   // Красный
      COLORREF greenColor = RGB(0, 255, 0);  // Зеленый
      COLORREF blueColor = RGB(0, 0, 255);   // Синий
      COLORREF yellowColor = RGB(255, 255, 0); // Желтый
      
Использование GDI32
Системное программирование
  • SetTextColor(HDC hdc, COLORREF color), SetBkColor(HDC hdc, COLORREF color):
    • Описание: SetTextColor устанавливает цвет текста. SetBkColor устанавливает цвет фона текста.

    • Использование: Настройка цветов текста.

    • Пример:

      SetTextColor(hdc, RGB(255, 0, 0));    // Устанавливаем красный цвет текста
      SetBkColor(hdc, RGB(0, 255, 255));      // Устанавливаем цвет фона текста в голубой (cyan)
      TextOut(hdc, 10, 350, "Red text on cyan background", 27);
      
Использование GDI32
Системное программирование

Ключевые моменты и советы:

  • Освобождение ресурсов: Обязательно освобождайте все созданные графические объекты (перья, кисти, шрифты, растровые изображения, контексты устройств) с помощью DeleteObject и DeleteDC, когда они больше не нужны. Это предотвращает утечки памяти.
  • Совместимость с кодировкой: Используйте LPCTSTR для строк, чтобы ваш код был совместим как с Unicode, так и с ANSI. Используйте макрос _T() или TEXT() для литеральных строк: TextOut(hdc, 10, 10, _T("Hello"), 5);.
Использование GDI32
Системное программирование
  • Обработка ошибок: Проверяйте возвращаемые значения функций GDI на ошибки (например, NULL).
  • Double Buffering: Используйте double buffering (off-screen buffer) для предотвращения мерцания при отрисовке сложных сцен.
  • Производительность: Функции SetPixel и GetPixel относительно медленные. Используйте растровые изображения и BitBlt для более быстрой отрисовки больших объемов пикселей.
Использование GDI32
Системное программирование

4. Примеры использования GDI32:

  • Рисование простых фигур: линии, прямоугольники, окружности.
  • Вывод текста с использованием различных шрифтов и цветов.
  • Отрисовка растровых изображений.
  • Реализация простейшего графического редактора (на уровне примитивов).
  • Double Buffering (двойная буферизация) для предотвращения мерцания.
Использование GDI32
Системное программирование

5. Оптимизация работы с GDI32:

  • Минимизация перерисовок.
  • Использование памяти эффективно.
  • Использование Off-Screen Bitmaps для сложной графики.
  • Использование GDI Objects мудро (создание и удаление).
Использование GDI32
Системное программирование

6. Проблемы и ограничения GDI32:

  • Производительность: GDI32 может быть недостаточно быстрым для сложных графических задач.
  • Ограниченная поддержка аппаратного ускорения.
  • Устаревание: Альтернативные API (Direct2D, OpenGL) предлагают более современные возможности.
Использование GDI32
Системное программирование

7. Альтернативы GDI32:

Direct2D:

  • Преимущества:
    • Аппаратное ускорение: использует GPU для отрисовки, что делает его значительно быстрее GDI32, особенно для сложных 2D сцен.
    • Высокое качество рендеринга: использует сглаживание субпикселей и другие техники для улучшения качества изображения.
    • Интеграция с DirectWrite: хорошо работает с DirectWrite для отрисовки текста с высоким качеством.
    • Легче в использовании, чем Direct3D.
  • Недостатки:
    • Зависимость от Windows: не является кроссплатформенным.
    • Не всегда оптимален для очень простых задач, где производительность GDI32 может быть достаточной.
Использование GDI32
Системное программирование

OpenGL:

  • Преимущества:
    • Кроссплатформенность: работает на Windows, macOS, Linux и других платформах.
    • Универсальность: подходит как для 2D, так и для 3D графики.
    • Широко распространен: большое сообщество и множество ресурсов.
  • Недостатки:
    • Более низкоуровневый API: может быть сложнее в использовании, чем Direct2D, особенно для простых 2D задач.
    • Зависимость от драйверов: производительность может сильно варьироваться в зависимости от качества драйверов видеокарты.
Использование GDI32
Системное программирование

Другие библиотеки (Qt, wxWidgets):

  • Преимущества:
    • Кроссплатформенность: позволяют создавать приложения, работающие на различных операционных системах.
    • Более высокий уровень абстракции: предоставляют упрощенный интерфейс для работы с графикой, что упрощает разработку.
    • Включают в себя множество других полезных компонентов: помимо графики, они также предоставляют виджеты, сетевые функции, работу с файлами и многое другое.
  • Недостатки:
    • Могут быть более ресурсоемкими: из-за дополнительной абстракции и функциональности.
    • Больший размер исполняемых файлов: библиотеки могут значительно увеличить размер вашего приложения.
    • Зависимость от фреймворка: приложение зависит от конкретного фреймворка, что может ограничить возможности.
Использование GDI32
Системное программирование

В заключение:

Выбор альтернативы GDI32 зависит от конкретных требований вашего проекта:

  • Direct2D: Лучший выбор для Windows-приложений, требующих высокой производительности 2D-графики и высокого качества рендеринга.
  • OpenGL: Отличный выбор для кроссплатформенных приложений, требующих как 2D, так и 3D графики.
  • Qt, wxWidgets: Подходят для кроссплатформенных приложений, которым требуется упрощенный интерфейс для работы с графикой и другие компоненты фреймворка.
Использование GDI32
Системное программирование

Примеры

Пример простой отрисовки:

#include <windows.h>

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
  HDC hdc;
  PAINTSTRUCT ps;

  switch (message) {
    case WM_PAINT:
      hdc = BeginPaint(hWnd, &ps);

      // Рисуем линию
      MoveToEx(hdc, 50, 50, NULL);
      LineTo(hdc, 200, 100);

      // Рисуем прямоугольник
      Rectangle(hdc, 100, 150, 300, 250);
Использование GDI32
Системное программирование
      // Выводим текст
      TextOut(hdc, 50, 300, "Hello, GDI32!", 13);

      EndPaint(hWnd, &ps);
      break;
    case WM_DESTROY:
      PostQuitMessage(0);
      break;
    default:
      return DefWindowProc(hWnd, message, wParam, lParam);
  }
  return 0;
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
  // (Код создания окна опущен для краткости)

  // Пример: Создание окна и обработка сообщений

  return 0;
}
Использование GDI32
Системное программирование

Пример двойной буферизации

#include <windows.h>

// Глобальные переменные
HWND hwnd; // Дескриптор окна
HDC hdc;    // Контекст устройства окна
HDC hdcBuffer; // Контекст устройства буфера
HBITMAP hBitmapBuffer; // Дескриптор Bitmap для буфера
RECT rect;   // Прямоугольник окна

// Функция для рисования в буфере
void DrawInBuffer(HDC hdc) {
  // Рисуем что-нибудь в буфере (например, случайные линии)
  for (int i = 0; i < 100; ++i) {
    int x1 = rand() % rect.right;
    int y1 = rand() % rect.bottom;
    int x2 = rand() % rect.right;
    int y2 = rand() % rect.bottom;

    MoveToEx(hdc, x1, y1, NULL);
    LineTo(hdc, x2, y2);
  }
}
Использование GDI32
Системное программирование
// Функция обновления экрана
void UpdateScreen() {
  // Получаем контекст устройства окна (только для копирования буфера)
  HDC hdcWindow = GetDC(hwnd);

  // Копируем содержимое буфера в окно
  BitBlt(hdcWindow, 0, 0, rect.right, rect.bottom, hdcBuffer, 0, 0, SRCCOPY);

  // Освобождаем контекст устройства окна
  ReleaseDC(hwnd, hdcWindow);
}
Использование GDI32
Системное программирование
// Функция обработки сообщений окна
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
  switch (message) {
    case WM_CREATE: {
      // Получаем контекст устройства окна
      hdc = GetDC(hWnd);
      hwnd = hWnd;

      // Получаем размеры клиентской области окна
      GetClientRect(hwnd, &rect);

      // Создаем совместимый контекст устройства для буфера
      hdcBuffer = CreateCompatibleDC(hdc);

      // Создаем Bitmap для буфера (с размерами клиентской области окна)
      hBitmapBuffer = CreateCompatibleBitmap(hdc, rect.right, rect.bottom);

      // Выбираем Bitmap в контекст устройства буфера
      SelectObject(hdcBuffer, hBitmapBuffer);

      // Освобождаем контекст устройства окна, полученный при WM_CREATE
      ReleaseDC(hwnd, hdc);

      break;
    }
Использование GDI32
Системное программирование
    case WM_PAINT: {
      PAINTSTRUCT ps;
      HDC hdcPaint = BeginPaint(hwnd, &ps);

      // Рисуем в буфере
      DrawInBuffer(hdcBuffer);

      // Обновляем экран (копируем из буфера)
      UpdateScreen();

      EndPaint(hwnd, &ps);
      break;
    }
Использование GDI32
Системное программирование
    case WM_SIZE: {
      // Получаем новые размеры клиентской области окна
      GetClientRect(hwnd, &rect);

      // Удаляем старый Bitmap
      DeleteObject(hBitmapBuffer);

      // Создаем новый Bitmap с новыми размерами
      HDC hdcWindow = GetDC(hwnd); // Получаем DC окна для совместимости
      hBitmapBuffer = CreateCompatibleBitmap(hdcWindow, rect.right, rect.bottom);
      ReleaseDC(hwnd, hdcWindow); // Освобождаем DC окна

      // Выбираем новый Bitmap в контекст устройства буфера
      SelectObject(hdcBuffer, hBitmapBuffer);

      break;
    }
Использование GDI32
Системное программирование
    case WM_DESTROY: {
      // Удаляем ресурсы
      DeleteObject(hBitmapBuffer);
      DeleteDC(hdcBuffer);
      PostQuitMessage(0);
      break;
    }

    default:
      return DefWindowProc(hWnd, message, wParam, lParam);
  }
  return 0;
}
Использование GDI32
Системное программирование
// WinMain
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
  // (Код создания окна опущен для краткости. Включает RegisterClassEx и CreateWindowEx)

  // ...

  // Пример: Создание окна, регистрация класса, и цикл обработки сообщений.
  MSG msg;
  while (GetMessage(&msg, NULL, 0, 0)) {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
  }

  return (int)msg.wParam;
}
Использование GDI32
Системное программирование

Пояснения:

  1. Глобальные переменные:
    • hwnd: Дескриптор окна.
    • hdc: (Устаревший) Контекст устройства окна. Рекомендуется получать DC при отрисовке и сразу освобождать, избегая хранения глобально.
    • hdcBuffer: Контекст устройства для off-screen buffer (буфера вне экрана).
    • hBitmapBuffer: Bitmap, связанный с hdcBuffer. В нем хранится графическое изображение.
    • rect: Прямоугольник, определяющий размеры клиентской области окна.
Использование GDI32
Системное программирование
  1. DrawInBuffer(HDC hdc):
    • Функция, которая рисует в контексте устройства буфера (hdcBuffer). Здесь происходит вся отрисовка. В примере рисуются случайные линии.
    • Важно: Эта функция не рисует напрямую на экране.
Использование GDI32
Системное программирование
  1. UpdateScreen():
    • Получает контекст устройства окна (hdcWindow). Важно: Контекст берется только для того, чтобы скопировать содержимое буфера на экран.
    • Использует BitBlt() для копирования содержимого hBitmapBuffer (через hdcBuffer) в окно.
    • Освобождает контекст устройства окна (ReleaseDC()).
Использование GDI32
Системное программирование
  1. WndProc() (Функция обработки сообщений окна):
    • WM_CREATE:
      • Более правильно: Получать DC окна (GetDC) и сразу освобождать (ReleaseDC) при создании. Это важно, чтобы избежать утечек ресурсов и конфликтов.
      • Создает hdcBuffer (совместимый с DC окна).
      • Создает hBitmapBuffer (с размерами окна) и выбирает его в hdcBuffer.
    • WM_PAINT:
      • Вызывается, когда окно нужно перерисовать.
      • Рисует в буфере (DrawInBuffer(hdcBuffer)).
      • Копирует содержимое буфера на экран (UpdateScreen()).
      • Использует BeginPaint() и EndPaint() для правильной обработки сообщения WM_PAINT.
Использование GDI32
Системное программирование
  • WM_SIZE:
    • Вызывается при изменении размера окна.
    • Удаляет старый hBitmapBuffer.
    • Создает новый hBitmapBuffer с новыми размерами.
    • Выбирает новый hBitmapBuffer в hdcBuffer. Важно пересоздавать буфер при изменении размеров окна!
  • WM_DESTROY:
    • Очищает все ресурсы (удаляет Bitmap и контекст устройства буфера).
Использование GDI32
Системное программирование
  1. WinMain():
    • Стандартная функция точки входа для Windows-приложения.
    • Код создания окна опущен для краткости (включает RegisterClassEx() и CreateWindowEx()).
    • Цикл обработки сообщений.
Использование GDI32
Системное программирование

Ключевые моменты двойной буферизации:

  • Отрисовка всегда происходит в off-screen буфере (hdcBuffer).
  • BitBlt() используется для быстрой копии содержимого буфера на экран. Это ключевой шаг.
  • Необходимо пересоздавать буфер (hBitmapBuffer) при изменении размера окна.
  • Получение и освобождение DC: Важно освобождать контекст устройства, как можно скорее, чтобы избежать утечек ресурсов. Желательно не хранить DC глобально.
Использование GDI32
Системное программирование

Как это работает (предотвращение мерцания):

Без двойной буферизации, когда вы рисуете напрямую на экране, процесс отрисовки может быть виден пользователю. Например, если вы рисуете сложные фигуры, экран может обновляться постепенно, что приводит к мерцанию.

С двойной буферизацией вы сначала рисуете всё изображение в off-screen буфере (который невидим для пользователя). Когда отрисовка завершена, вы копируете полностью готовое изображение на экран одним быстрым BitBlt(). Пользователь видит только конечный результат, и мерцание устраняется.

Важно: Этот пример использует GDI32. Для более производительной графики рассмотрите использование Direct2D или OpenGL. GDI32 может быть медленным для сложных операций, особенно если аппаратное ускорение не используется.

Использование GDI32
Системное программирование

Пример отрисовки битмапа с прозрачным фоном

#include <windows.h>
#include <wingdi.h>  // Необходим для BLENDFUNCTION

// Глобальные переменные
HWND hwnd; // Дескриптор окна
HBITMAP hBitmap; // Дескриптор Bitmap с прозрачным фоном

// Функция загрузки Bitmap с прозрачным фоном (Magenta)
HBITMAP LoadTransparentBitmap(HDC hdc, HINSTANCE hInstance, LPCWSTR bitmapResourceName, COLORREF transparentColor) {
    HBITMAP hBitmapLoaded = LoadBitmapW(hInstance, bitmapResourceName);
    if (!hBitmapLoaded) {
        return NULL;
    }

    BITMAP bm;
    GetObject(hBitmapLoaded, sizeof(bm), &bm);
Использование GDI32
Системное программирование
    // Создаем Bitmap с маской
    HBITMAP hBitmapMask = CreateBitmap(bm.bmWidth, bm.bmHeight, 1, 1, NULL);
    if (!hBitmapMask) {
        DeleteObject(hBitmapLoaded);
        return NULL;
    }

    // Создаем DC для Bitmap и маски
    HDC hdcMem = CreateCompatibleDC(hdc);
    HDC hdcMask = CreateCompatibleDC(hdc);

    if (!hdcMem || !hdcMask) {
        DeleteDC(hdcMem);
        DeleteDC(hdcMask);
        DeleteObject(hBitmapLoaded);
        DeleteObject(hBitmapMask);
        return NULL;
    }
Использование GDI32
Системное программирование
    HBITMAP hBitmapOldMem = (HBITMAP)SelectObject(hdcMem, hBitmapLoaded);
    HBITMAP hBitmapOldMask = (HBITMAP)SelectObject(hdcMask, hBitmapMask);

    // Создаем маску, закрашивая прозрачный цвет черным
    SetBkColor(hdcMem, transparentColor);
    BitBlt(hdcMask, 0, 0, bm.bmWidth, bm.bmHeight, hdcMem, 0, 0, SRCCOPY);

    // Заменяем прозрачный цвет на белый в оригинальном Bitmap
    SetBkColor(hdcMem, RGB(0, 0, 0)); // Black
    SetTextColor(hdcMem, RGB(255, 255, 255)); // White
    BitBlt(hdcMem, 0, 0, bm.bmWidth, bm.bmHeight, hdcMask, 0, 0, SRCAND);

    // Устанавливаем черную маску для прозрачности
    SetBkColor(hdcMem, RGB(255, 255, 255)); // White
    SetTextColor(hdcMem, RGB(0, 0, 0)); // Black
    BitBlt(hdcMem, 0, 0, bm.bmWidth, bm.bmHeight, hdcMask, 0, 0, SRCPAINT);
Использование GDI32
Системное программирование
    // Восстанавливаем старые объекты в DC
    SelectObject(hdcMem, hBitmapOldMem);
    SelectObject(hdcMask, hBitmapOldMask);

    // Удаляем временные DC и маску
    DeleteDC(hdcMem);
    DeleteDC(hdcMask);
    DeleteObject(hBitmapMask);

    return hBitmapLoaded;
}

Использование GDI32
Системное программирование
// Функция обработки сообщений окна
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
    PAINTSTRUCT ps;
    HDC hdc;

    switch (message) {
        case WM_CREATE: {
            // Загружаем Bitmap
            HINSTANCE hInstance = GetModuleHandle(NULL);
            hdc = GetDC(hWnd);
            hBitmap = LoadTransparentBitmap(hdc, hInstance, MAKEINTRESOURCEW(IDB_TRANSPARENT_BITMAP), RGB(255, 0, 255)); // Magenta as transparent color
            ReleaseDC(hWnd, hdc);

            if (!hBitmap) {
                MessageBoxW(hWnd, L"Failed to load bitmap!", L"Error", MB_OK | MB_ICONERROR);
                PostQuitMessage(0);
                return -1;
            }
            break;
        }
Использование GDI32
Системное программирование
        case WM_PAINT:
            hdc = BeginPaint(hWnd, &ps);

            if (hBitmap) {
                // Получаем размеры Bitmap
                BITMAP bm;
                GetObject(hBitmap, sizeof(bm), &bm);

                // Создаем DC для Bitmap
                HDC hdcMem = CreateCompatibleDC(hdc);
                HBITMAP hBitmapOld = (HBITMAP)SelectObject(hdcMem, hBitmap);

                // Рисуем Bitmap с прозрачным фоном
                TransparentBlt(hdc, 10, 10, bm.bmWidth, bm.bmHeight, hdcMem, 0, 0, bm.bmWidth, bm.bmHeight, RGB(255, 0, 255)); // Magenta

                // Восстанавливаем старый Bitmap
                SelectObject(hdcMem, hBitmapOld);

                // Удаляем временный DC
                DeleteDC(hdcMem);
            }

            EndPaint(hWnd, &ps);
            break;
Использование GDI32
Системное программирование
        case WM_DESTROY:
            // Освобождаем ресурсы
            if (hBitmap) {
                DeleteObject(hBitmap);
            }
            PostQuitMessage(0);
            break;

        default:
            return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}
Использование GDI32
Системное программирование
// WinMain
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
    // (Код создания окна опущен для краткости)
    // Необходимо зарегистрировать класс окна и создать окно.

    WNDCLASSEXW wcex;

    wcex.cbSize = sizeof(WNDCLASSEX);
    wcex.style = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc = WndProc;
    wcex.cbClsExtra = 0;
    wcex.cbWndExtra = 0;
    wcex.hInstance = hInstance;
    wcex.hIcon = LoadIconW(hInstance, MAKEINTRESOURCEW(IDI_APPLICATION));
    wcex.hCursor = LoadCursorW(nullptr, IDC_ARROW);
    wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    wcex.lpszMenuName = nullptr;
    wcex.lpszClassName = L"TransparentBitmapClass";
    wcex.hIconSm = LoadIconW(wcex.hInstance, MAKEINTRESOURCEW(IDI_APPLICATION));

    RegisterClassExW(&wcex);
Использование GDI32
Системное программирование
    hwnd = CreateWindowExW(
        0,
        L"TransparentBitmapClass",
        L"Transparent Bitmap Example",
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, CW_USEDEFAULT,
        500, 400,
        nullptr,
        nullptr,
        hInstance,
        nullptr);


    if (!hwnd) {
        return FALSE;
    }

    ShowWindow(hwnd, nCmdShow);
    UpdateWindow(hwnd);

Использование GDI32
Системное программирование

    // Цикл обработки сообщений
    MSG msg;
    while (GetMessage(&msg, nullptr, 0, 0)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return (int)msg.wParam;
}
Использование GDI32
Системное программирование

Пояснения:

  1. LoadTransparentBitmap(HDC hdc, HINSTANCE hInstance, LPCWSTR bitmapResourceName, COLORREF transparentColor):
    • Эта функция загружает Bitmap из ресурса и делает указанный цвет прозрачным. В данном примере используется цвет Magenta (RGB(255, 0, 255)) в качестве прозрачного.
    • Создание маски: Функция создает битовую маску, где прозрачный цвет представлен черным, а остальные - белым. Эта маска используется для определения, какие части bitmap должны быть прозрачными.
    • Применение маски: Функция использует логические операции SRCAND и SRCPAINT для применения маски к bitmap, делая прозрачные области bitmap прозрачными.
    • Возвращает дескриптор загруженного Bitmap (HBITMAP). Функция также создает и удаляет временные DC и маски.
Использование GDI32
Системное программирование
  1. WndProc() (Функция обработки сообщений окна):
    • WM_CREATE: Загружает Bitmap с помощью LoadTransparentBitmap() и сохраняет его дескриптор в глобальной переменной hBitmap.
    • WM_PAINT:
      • Создает совместимый DC (hdcMem) для работы с Bitmap.
      • Выбирает Bitmap в hdcMem.
      • TransparentBlt(): Ключевая функция, которая выполняет копирование Bitmap с учетом прозрачности. Параметры:
        • hdc: Контекст устройства, куда рисуем (окно).
        • 10, 10: Координаты верхнего левого угла, куда будет нарисован Bitmap.
        • bm.bmWidth, bm.bmHeight: Ширина и высота Bitmap.
        • hdcMem: Контекст устройства, из которого берется Bitmap.
        • 0, 0: Координаты верхнего левого угла Bitmap для копирования.
        • bm.bmWidth, bm.bmHeight: Ширина и высота Bitmap для копирования.
        • RGB(255, 0, 255): Цвет, который будет считаться прозрачным.
      • Очищает ресурсы (удаляет временный DC).
    • WM_DESTROY: Освобождает ресурсы, удаляя Bitmap.
Использование GDI32
Системное программирование
  1. WinMain():
    • Стандартная функция точки входа.
    • Важно: Убедитесь, что ресурс Bitmap (IDB_TRANSPARENT_BITMAP) добавлен в файл ресурсов (.rc) вашего проекта. Иначе, загрузка bitmap не удастся.
Использование GDI32
Системное программирование

Как это работает (прозрачность):

TransparentBlt() использует указанный цвет (в примере Magenta) как ключ прозрачности. Когда она копирует пиксель из исходного Bitmap, она проверяет, соответствует ли цвет пикселя указанному цвету прозрачности. Если да, то пиксель не копируется, и вместо него остается фон целевого контекста устройства. Таким образом, создается эффект прозрачности.

Использование GDI32
Системное программирование

Важные моменты:

  • Файл ресурсов (.rc): Не забудьте добавить Bitmap в файл ресурсов вашего проекта.
    • В Visual Studio: View -> Resource View, затем Insert -> Resource -> Bitmap, Import. Укажите путь к вашему файлу Bitmap. Укажите ID (например, IDB_TRANSPARENT_BITMAP).
  • #define: Определите ID ресурса в заголовочном файле (например, Resource.h): #define IDB_TRANSPARENT_BITMAP 101
  • Magenta (RGB(255, 0, 255)): Часто используется в качестве стандартного цвета для прозрачности, но вы можете использовать любой другой цвет, который не встречается в остальной части вашего Bitmap.
  • TransparentBlt(): Функция GDI32, предназначенная для копирования регионов растровых изображений, обеспечивая прозрачность по указанному цвету.
Использование GDI32
Системное программирование

Альтернативные подходы (и более современные):

  • Alpha Blending: Использование Bitmap с альфа-каналом (32-битный Bitmap) для более гладкой прозрачности. Для этого можно использовать AlphaBlend(). Alpha-канал определяет уровень прозрачности для каждого пикселя. Это более гибкий подход, чем использование одного цвета в качестве прозрачного.
  • Direct2D: Direct2D является более современным API для 2D-графики в Windows. Он поддерживает аппаратное ускорение и предоставляет более эффективные способы работы с прозрачностью и Bitmap.
  • GDI+: Предоставляет более продвинутые методы работы с изображениями, включая альфа-канал.
Использование GDI32